package com.owner.lt.tool.tool;

import com.mysql.cj.jdbc.result.ResultSetMetaData;
import com.owner.lt.tool.annotation.Column;
import com.owner.lt.tool.utils.CollectionUtils;
import com.owner.lt.tool.utils.JDBCUtils;
import com.owner.lt.tool.utils.StringUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.sql.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class ExecQueryTool {

    private Connection connection;

    public ExecQueryTool(Connection connection) {
        this.connection = connection;
    }

    public <T> List<T> executeQuery(String sql, Class<T> clazz, Object... paramList) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        List<T> resultList = new ArrayList<>();
        sql = sql.trim();
        try {
            JDBCUtils.validateSQL(sql);
            // 创建 SQL 语句执行器
            preparedStatement = connection.prepareStatement(sql);

            JDBCUtils.extracted(paramList, preparedStatement);
            // 执行 SQL 语句并获取结果集
            resultSet = preparedStatement.executeQuery();
            // 获取所有字段名并转为小写
            ResultSetMetaData resultSetMetaData = (ResultSetMetaData) resultSet.getMetaData();
            List<String> filedList = Arrays.stream(resultSetMetaData.getFields()).map(filed -> filed.getName().toLowerCase()).collect(Collectors.toList());

            // 将结果集转换为 List
            while (resultSet.next()) {
                // 根据不同的 T 类型，将结果集的列值转换为相应的对象
                T obj = convertResultSetToObject(resultSet, clazz, filedList);
                resultList.add(obj);
            }

        } catch (SQLException e) {
            log.warn("SQL执行出错", e);
            return null;
        } catch (NoSuchFieldException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭资源
            JDBCUtils.close(resultSet, preparedStatement, null);
        }
        return resultList;
    }


    public <O> O executeQueryFindOne(String sql, Class<O> clazz, Object... params) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        sql = sql.trim();
        try {
            JDBCUtils.validateSQL(sql);
            // 创建 SQL 语句执行器
            preparedStatement = connection.prepareStatement(sql);

            JDBCUtils.extracted(params, preparedStatement);
            // 执行 SQL 语句并获取结果集
            resultSet = preparedStatement.executeQuery();
            // 获取所有字段名并转为小写
            ResultSetMetaData resultSetMetaData = (ResultSetMetaData) resultSet.getMetaData();
            List<String> filedList = Arrays.stream(resultSetMetaData.getFields()).map(filed -> filed.getName().toLowerCase()).collect(Collectors.toList());

            if (resultSet.next()) {
                O obj = convertResultSetToObject(resultSet, clazz, filedList);
                return obj;
            }
        } catch (SQLException e) {
            log.warn("SQL执行出错", e);
            return null;
        } catch (NoSuchFieldException | InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        } finally {
            // 关闭资源
            JDBCUtils.close(resultSet, preparedStatement, null);
        }
        return null;
    }

    /**
     * @param resultSet 需要转换的结果集
     * @param objClass  返回的对象
     * @param fieldList 查询语句中的字段名
     * @param <T>
     * @return
     * @throws SQLException
     * @throws NoSuchFieldException
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private <T> T convertResultSetToObject(ResultSet resultSet, Class<T> objClass, List<String> fieldList) throws SQLException, NoSuchFieldException, InstantiationException, IllegalAccessException {
        T t = objClass.newInstance();
        List<String> fieldNames = getFieldNames(objClass);

        for (String fieldName : fieldNames) {

            Field field = objClass.getDeclaredField(fieldName);
            Column annotation = field.getAnnotation(Column.class);
            // 将此对象的可访问标志设置为指示的布尔值。值true表示反射对象在使用时应禁止Java语言访问检查。值false表示反射的对象应该强制执行Java语言访问检查。
            field.setAccessible(true);
            if (annotation == null || StringUtils.isBlank(annotation.value())) {
                if (!fieldList.contains(fieldName)) {
                    continue;
                }
                field.set(t, resultSet.getObject(fieldName) == null ? null : resultSet.getObject(fieldName));
            } else {
                if (!fieldList.contains(annotation.value())) {
                    continue;
                }
                field.set(t, resultSet.getObject(annotation.value()) == null ? null : resultSet.getObject(annotation.value()));
            }
        }
        return t;
    }

    public static List<String> getFieldNames(Class<?> clazz) {
        List<String> fieldNames = new ArrayList<>();
        for (Field field : clazz.getDeclaredFields()) {
            if (!Modifier.isStatic(field.getModifiers())) {
                fieldNames.add(field.getName());
            }
        }
        return fieldNames;
    }

}
